
package hasthi.common;


import javax.xml.namespace.QName;

import hasthi.common.constants.WSDMConstants;

import org.apache.xmlbeans.XmlObject;
import org.w3.x2003.x05.soapEnvelope.Fault;
import org.w3.x2003.x05.soapEnvelope.FaultDocument;
import org.xmlpull.v1.builder.XmlElement;
import org.xmlpull.v1.builder.XmlNamespace;

import xsul.XmlConstants;
import xsul.soap.SoapUtil;
import xsul.soap11_util.Soap11Util;
import xsul.soap12_util.Soap12Util;
import xsul.xbeans_util.XBeansUtil;
import edu.indiana.extreme.wsdm.ServiceErrorInformationDocument;

public class HasthiException extends Exception{
    /**
     * 
     */
    private static final long serialVersionUID = -1173007724679454589L;

    public static enum FaultCode{ServiceNotReachable, InvaliedRequest, InternalServiceError, ErrorAtDependentService, ErrorAtClientBeforeWsCall, 
        ErrorAtClientWhileWsCall, ErrorAtClientAfterWsCall, InitalizationError, UnsupportedMessage, InvaliedLocalArgumnet, LocalError,ConfigError,InstrumentationError};
    
    
    public static String FAULT_NAMESPACE = "http://extreme.indiana.edu/management/faults";
    public static String FAULT_DETAILS_NAME = "faultParameters";
    
    public ServiceErrorInformationDocument getErrorAsXml(){
        ServiceErrorInformationDocument serviceErrorInformationDocument = ServiceErrorInformationDocument.Factory.newInstance();
        serviceErrorInformationDocument.addNewServiceErrorInformation();
        return serviceErrorInformationDocument;
    }
    
    protected FaultCode faultCode;
    private XmlElement propertyData;
    

    public static HasthiException buildException(XmlObject soapErr){
        String faultCodeAsStr;
        String message;
        Exception cause;
        //TODO try to fill remote stacktrace
        if(soapErr instanceof FaultDocument ){
            Fault fault = ((FaultDocument)soapErr).getFault();
            faultCodeAsStr = fault.getCode().getValue().getLocalPart();
            if(fault.getReason().getTextArray() != null && fault.getReason().getTextArray().length > 0 ){
                message = fault.getReason().getTextArray()[0].getStringValue();
            }else{
                message = "UnspecifiedErrorByTheServer";
            }
            cause = new Exception(XmlBeanUtils.prettyPrint(fault.getDetail()));
            cause.setStackTrace(new StackTraceElement[0]);
        }else if(soapErr instanceof org.xmlsoap.schemas.soap.envelope.FaultDocument ){
            org.xmlsoap.schemas.soap.envelope.Fault fault = ((org.xmlsoap.schemas.soap.envelope.FaultDocument)soapErr).getFault();
            faultCodeAsStr = fault.getFaultcode().getLocalPart();
            message = fault.getFaultstring();
            cause = new Exception(XmlBeanUtils.prettyPrint(fault.getDetail()));
            cause.setStackTrace(new StackTraceElement[0]);
        }else{
            throw new HasthiError("Unknown Exception type "+ soapErr);
        }

        FaultCode faultCode;
        try {
            faultCode = FaultCode.valueOf(faultCodeAsStr);
        } catch (IllegalArgumentException e) {
            faultCode = FaultCode.InternalServiceError; 
        }
        
       
        
        RemoteProcessorException error = new RemoteProcessorException(message,cause,faultCode);
        XmlObject propertyDataAsXmlBeans = XmlBeanUtils.findByXpath(new QName(FAULT_NAMESPACE,FAULT_DETAILS_NAME), soapErr);
        if(propertyDataAsXmlBeans != null){
            error.addPropertyData(XBeansUtil.xmlObjectToXmlElement(propertyDataAsXmlBeans));    
        }
        return error;
    }
    
    
    public HasthiException(String message, Throwable cause, FaultCode faultCode) {
        super(message, cause);
        this.faultCode = faultCode;
    }

    public HasthiException(String message, FaultCode faultCode) {
        super(message);
        this.faultCode = faultCode;
    }

    public HasthiException(Throwable cause, FaultCode faultCode) {
        super(cause);
        if(cause instanceof HasthiException){
            HasthiException e = (HasthiException)cause;
            setFaultCode(e.getFaultCode());
            this.propertyData = e.propertyData;
        }
        this.faultCode = faultCode;
    }
    public FaultCode getFaultCode() {
        return faultCode;
    }
    
    public void addProperty(String name,String value){
        if(propertyData == null){
            propertyData = XmlConstants.BUILDER.newFragment(FAULT_NAMESPACE, FAULT_DETAILS_NAME);
        }
        propertyData.addElement(name).addChild(value);
    }
    
    public String getProperty(String name){
        if(propertyData != null){
            XmlElement element = propertyData.element(null, name);
            return element == null? null:element.requiredTextContent();
        }
        return null;
    }
    
    public void addPropertyData(XmlElement propertyData){
        this.propertyData = propertyData;
    }
    
    public XmlObject createFaultAsXmlBeans(boolean soap12){
        if(soap12){
            XmlElement fault = createFault(Soap12Util.getInstance());
            return XBeansUtil.xmlElementToXmlObject(fault); 
        }else{
            XmlElement fault = createFault(Soap11Util.getInstance());
            return XBeansUtil.xmlElementToXmlObject(fault); 
        }
        
    }
    
    public XmlElement createFault(SoapUtil soapUtil){
        XmlNamespace gfacNs = XmlConstants.BUILDER.newNamespace(WSDMConstants.EXTREME_MANAGEMENT_NS);
        XmlElement fault = soapUtil.generateSoapFault(gfacNs, getFaultCode().toString(), getMessage(), this);
        if(propertyData != null){
            XmlElement faultDetails = fault.element(null,"Detail");
            if(faultDetails == null){
                faultDetails = fault.element(null,"detail");
            }
            if(faultDetails != null){
                faultDetails.addChild(propertyData);    
            }
        }
        return fault;
    }

    private void setFaultCode(FaultCode faultCode) {
        this.faultCode = faultCode;
    }


    @Override
    public String getMessage() {
        String msgStr = super.getMessage();
        if(!msgStr.startsWith("["+faultCode)){
            StringBuffer message = new StringBuffer();
            message.append("[").append(faultCode).append("]");
            message.append(msgStr);
            return message.toString();            
        }else{
            return msgStr;
        }

    }
}

